package cn._2dland.leech;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.params.HttpClientParams;

import cn._2dland.leech.download.IDMDownloader;
import cn._2dland.leech.util.LeechUtils;
import cn._2dland.util.Logger;

/**
 * 基于Leech网站的客户端
 * 
 * @author deadblue
 */
public abstract class LeechClient {
	/** 内置HTTP客户端 */
	protected HttpClient client = null;

	public LeechClient() {
		// 初始化HTTP客户端
		client = new HttpClient();
		client.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
		client.getParams().setBooleanParameter(HttpClientParams.SINGLE_COOKIE_HEADER, true);
		client.getParams().setParameter(HttpClientParams.USER_AGENT, "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)");
	}

	/**
	 * 通过leech服务器下载网盘文件
	 * @param url
	 */
	public boolean download(String url, String savePath) {
		boolean fOK = false;
		try {
			if(needLogin()) {
				Logger.debug("login...");
				login();
			}

			Logger.info("准备下载: " + url);
			NameValuePair[] params = parse(url);
			if(params == null || params.length <= 0)
				throw new Exception("获取下载参数失败!");

			Logger.info("服务端开始下载文件...");
			url = transload(url, params);

			Logger.info("服务端下载完成! 开始下载到本地...");
			save(url, savePath);
			Logger.info("文件下载完成!");

			fOK = true;
		} catch(Exception e) {
			e.printStackTrace(System.err);
			fOK = false;
		}
		return fOK;
	}

	/**
	 * 将下载地址发送到解析接口，并获取第二步提交所需的参数
	 * @param url
	 * @return
	 * @throws Exception
	 */
	protected NameValuePair[] parse(String url) throws Exception {
		NameValuePair[] params2 = null;

		PostMethod post = new PostMethod(getParseURL(url));
		try {
			// 设置引用页
			String referer = getParseReferer(url);
			if(referer != null)
				post.addRequestHeader("Referer", referer);

			// 设置参数
			post.addParameters(getParseParams(url));

			// 请求参数
			int respCode = client.executeMethod(post);
			if(respCode != HttpStatus.SC_OK)
				throw new HTTPException(respCode);

			// 解析返回结果
			params2 = getTransloadParams(post.getResponseBodyAsStream());
		} finally {
			post.releaseConnection();
		}

		return params2;
	}

	/**
	 * 执行传输，将目标文件下载到服务器端，并返回下载地址
	 * @param params
	 * @return
	 * @throws Exception
	 */
	protected String transload(String url, NameValuePair[] params) throws Exception {
		String downURL = null;

		PostMethod post = new PostMethod(getTransloadURL(url));
		try {
			post.setRequestHeader("Referer", getParseURL(url));
			post.addParameters(params);

			int respCode = client.executeMethod(post);
			if(respCode != HttpStatus.SC_OK)
				throw new HTTPException(respCode);

			String respBody = LeechUtils.readResopnseStream(post.getResponseBodyAsStream());
			downURL = parseDownloadURL(url, respBody);
		} finally {
			post.releaseConnection();
		}

		return downURL;
	}

	/**
	 * 将文件下载到本地
	 * @param url
	 * @throws Exception
	 */
	protected void save(String url, String savePath) throws Exception {
		Logger.debug("下载地址: " + url);
		
		IDownloader d = new IDMDownloader();
		d.save(url, null, savePath);
	}

	/**
	 * 是否需要登录
	 * @return
	 */
	protected boolean needLogin() {
		return false;
	}

	/**
	 * 执行登录动作
	 */
	protected void login() throws Exception {}
	
	/**
	 * 获取解析API的地址
	 * @param url
	 * @return
	 */
	abstract protected String getParseURL(String url);
	
	/**
	 * 获取解析API的引用页
	 * @param url
	 * @return
	 */
	abstract protected String getParseReferer(String url);

	/**
	 * 获取解析API的参数表
	 * @param url
	 * @return
	 */
	abstract protected NameValuePair[] getParseParams(String url);
	
	/**
	 * 从返回的HTML中抽取第二步提交的参数
	 * @param is
	 * @return
	 */
	protected NameValuePair[] getTransloadParams(InputStream in) throws Exception {
		List<NameValuePair> pms = new ArrayList<NameValuePair>();

		// TODO 算法需要调整，一个input并不一定在同一行
		BufferedReader br = new BufferedReader(new InputStreamReader(in));
		for(String line; (line=br.readLine())!=null; ) {
			line = line.trim();
			// 跳过非表单行
			if(!line.startsWith("<input type=\"hidden\"")) 
				continue;
			
			// 解析出名称和值
			NameValuePair pair = parseHiddenInput(line);
			pms.add(pair);

			Logger.debug(pair);
		}
		br.close();

		return pms.toArray(new NameValuePair[]{});
	}

	/**
	 * 获取传输API地址
	 * @param url
	 * @return
	 */
	abstract protected String getTransloadURL(String url);
	
	/**
	 * 从返回的HTML中获取下载地址
	 * @param body
	 * @return
	 * @throws Exception
	 */
	abstract protected String parseDownloadURL(String url, String body);

	/**
	 * 从URL中获取文件名
	 * @param url
	 * @return
	 */
	protected String getFileName(String url) {
		String fileName = url.substring(url.lastIndexOf('/'));
		try {
			fileName = URLDecoder.decode(fileName, "ISO8859-1");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return fileName;
	}

	/**
	 * 解析hidden input的html代码，抽取出名称和值
	 * @param html
	 * @return
	 */
	private NameValuePair parseHiddenInput(String html) {
		NameValuePair pair = new NameValuePair("", "");

		// 提取参数名
		Matcher m = Pattern.compile("name=\"\\w+\"").matcher(html);
		if(m.find()) {
			pair.setName(html.substring(m.start() + 6, m.end() - 1));
		}
		// 提取参数值
		m = Pattern.compile(" value=\"").matcher(html);
		if(m.find()) {
			pair.setValue(html.substring(m.end(), html.lastIndexOf('"')));
		}
		return pair;
	}

}
